#include <AST/expr/function/FunctionAST.hpp>
#include <global.hpp>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/raw_ostream.h>
#include <AST/expr/var/VarAST.hpp>

FunctionAST::FunctionAST(PrototypeAST *proto, Expr *body)
    : proto(proto), body(body) {
    // assert(proto!=nullptr && "PrototypeAST *proto should not be Null");
    // assert(body!=nullptr && "Expr *body should not be Null");
    if (body == nullptr) { printf("Expr *body should not be Null"); }
}

llvm::Function *FunctionAST::codegen() {
    using llvm::Argument;
    using llvm::AllocaInst;
    // 检查是否已有声明
    // FIXME: 检查签名是否不同
    Function *fun = theModule.getFunction(proto->getFunName());

    if (fun == nullptr) fun = proto->codegen();

    if (fun == nullptr) return nullptr;

    // 检查重复定义
    if (!fun->empty()) {
        std::cerr << fun->getName().str() << std::endl;
        logError("FunctionAST::codegen: Function redefining");
        return nullptr;
    }

    // 创建新Block
    BasicBlock *block = BasicBlock::Create(theContext, "entry", fun, nullptr);
    builder.SetInsertPoint(block);

    // 记录局部变量
    // 全局函数，变量符号表应当为空
    assert(theScope.isClear());
    // 创建新作用域
    auto scopeGuard = theScope.guard();
    for (unsigned int i = 0; i < fun->arg_size(); ++i) {
        // LLVM arg 以 Value 形式出现
        Argument *arg = fun->getArg(i);
        const string argName = arg->getName().str();
        if (proto->getParaIsRef(i)) {
            // 情况：按引用传递，则无需alloc，arg是地址，且地址不可修改
            if (!theScope.insertValue(argName, arg)) {
                logError(
                    "FunctionAST::codegen: redefinition found: " + argName);
                return nullptr;
            }
        } else {
            // 情况：按值传递，需要新建局部变量
            Var paraVar = Var(arg);
            // 生成栈空间
            AllocaInst *alloc = paraVar.createEntryAlloca(fun);
            // 储存初始值
            builder.CreateStore(arg, alloc);
            // 添加到符号表
            if (!theScope.insertValue(argName, alloc)) {
                logError(
                    "FunctionAST::codegen: redefinition found: " + argName);
                return nullptr;
            }
        }
    }

    if (body == nullptr)
        return static_cast<llvm::Function *>(
            logError("FunctionAST::codegen: body is emptr"));

    if (Value *ret = body->codegen(); ret != nullptr) {
        if (builder.GetInsertBlock()->getTerminator() == nullptr) {
            // 如果最后没有返回，则自动创建一个
            if (fun->getReturnType() == BasicType::VoidTy)
                builder.CreateRetVoid();
            else
                builder.CreateRet(ret);
        }

        if (llvm::verifyFunction(*fun, &(llvm::errs()))) {
            logError("FunctionAST::codegen: verifyFunction failed");
            fun->print(llvm::errs());
            fun->eraseFromParent();
            return nullptr;
        }

        theFPM.run(*fun);

        return fun;
    }

    logError("FunctionAST::codegen: body gen failed");
    return nullptr;
}
